package com.xinsoft.websocket;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.xinsoft.proxy.RequestContextProxy;
import com.xinsoft.utils.SpringUtils;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author Li9527
 * @Description 每个连接单独持有一个各自的WebSocketHandler，使用Spring时需要注意注入问题
 * @Date 2023/4/23 8:57
 **/
@Api(tags = "websocket--WebSocket连接前段和相关处理方法。")
@ServerEndpoint("/websocket/{uid}")
@Slf4j
@Component
public class WebSocketHandler {

    /**
     * 心跳消息
     */
    private static final String PING_MESSAGE = "ping";
    private static final String PONG_MESSAGE = "pong";

    //    @Autowired
    private static WebSocketSessionHolderManager sessionHolderManager = SpringUtils.getBean(WebSocketSessionHolderManager.class);
//    WebSocketSessionHolderManager sessionHolderManager;

    /**
     * 当建立连接时触发
     *
     * @param session: 会话
     * @param uid:   用户uid
     * @return void
     * @description
     * @author Li9527
     * @date 2023年4月23日09:19:14
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("uid") String uid) throws IOException {
        // 第一阶段调试，写死上下文代理，下一阶段，直接在调用会话认证切面中jwt鉴权后注入上下文对象
        Map<String, Object> userMap = new HashMap<>();
//        userMap.put("userName", "Li9527");
//        userMap.put("userRealName", "李杰");
//        userMap.put("userPassword", "123456");
        // 查出用户放进去
        userMap.put("userId", uid);
        RequestContextProxy.setContextMap(userMap);
        Map<String, Object> contextMap = RequestContextProxy.getContextMap();
        if (CollUtil.isEmpty(contextMap)) {
            log.debug("连接鉴权失败，关闭连接，uid: {}", uid);
            // 获取失败 关闭当前连接
            session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "登录失败"));
            return;
        }

        // 判断当前用户是否已经存在连接
        WebSocketSessionHolder oldHolder = sessionHolderManager.get(uid);
        if (oldHolder != null) {
            log.debug("该用户已连接，旧连接将关闭，uid: {}", uid);
            // 如果存在，关闭旧连接，但是不阻止新连接
            Session oldSession = oldHolder.getSession();
            oldSession.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "您已经在另一处登录"));
        }

        // 新连接处理 保存会话和相关信息
        Date now = DateUtil.date();
        WebSocketSessionHolder holder = new WebSocketSessionHolder();
        holder.setUserId(uid);
        holder.setSession(session);
        holder.setLoginTime(now);
        holder.setLastPingTime(now);

        sessionHolderManager.save(uid, holder);
        session.getAsyncRemote().sendText("连接成功");
        log.debug("userId= {}鉴权成功，已建立连接", uid);
    }

    /**
     * 当接收消息时触发
     *
     * @param message:
     * @param session:
     * @return void
     * @description
     * @author Uyuhz
     * @date 2021/5/14 13:41
     */
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        WebSocketSessionHolder holder = sessionHolderManager.get(session);
        log.debug("userId= {} 接收到消息，msg= {}", holder == null ? "未知" : holder.getUserId(), message);
        if (holder == null) {
            session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "未知的用户"));
            return;
        }
        if (StrUtil.equals(message, PING_MESSAGE)) {
            // 心跳消息
            holder.setLastPingTime(DateUtil.date());
            sessionHolderManager.sendMessage(holder.getUserId(), PONG_MESSAGE);
        } else {
            // 有阻塞影响
            // 目前除了心跳消息，接收的只有GPS数据
//            gpsMsgService.handleGpsMsg(message);
            sessionHolderManager.sendMessage(holder.getUserId(), message + "test");
        }
    }

    /**
     * 当异常时触发
     *
     * @param session:
     * @param error:
     * @return void
     * @description
     * @author Uyuhz
     * @date 2021/5/14 13:41
     */
    @OnError
    public void onError(Session session, Throwable error) throws IOException {
        WebSocketSessionHolder holder = sessionHolderManager.get(session);
        if (holder == null) {
            // 未登录连接，异常时直接关闭
            if (session.isOpen()) {
                session.close(new CloseReason(CloseReason.CloseCodes.TRY_AGAIN_LATER, "未知异常"));
            }
            log.error("WebSocket异常，未登录用户，连接已关闭", error);
        } else {
            log.error("WebSocket异常，userId= {}，连接地址= {}", holder.getUserId(), session.getRequestURI(), error);
        }
    }

    /**
     * 当连接关闭时触发
     *
     * @param session:
     * @return void
     * @description
     * @author Uyuhz
     * @date 2021/5/14 13:41
     */
    @OnClose
    public void onClose(Session session) {
        WebSocketSessionHolder holder = sessionHolderManager.get(session);
        log.debug("userId= {} 连接关闭", holder == null ? "未知" : holder.getUserId());
        // 移除连接
        sessionHolderManager.remove(session);
    }

    /**
     * 取用户id
     *
     * @param :
     * @return boolean
     * @description
     * @author Uyuhz
     * @date 2021/5/14 10:34
     */
    private String getCurrentUserId(String token) {
        if (StrUtil.isNotBlank(token)) {
            return "1";
//            OcnUser ocnUser = tokenService.getCurrentUser(token);
//            if (ocnUser != null) {
//                return ocnUser.getId();
//            }
        }
        return null;
    }
}
